package PokerTP;

import java.util.Random;

public class PlayTable extends Table {
	private long tableId;
	private long currentPlayerId = 0;
	private PlayerInfo[] players;
	private int bots;
	private int humans;
	private String tableName;
	private int entranceFee;
	private int tokensCount;
	private long makerId = 0;
	private int playerNum = -1;
	private boolean started = false;
	private int playersWithDoneAction = 0;
	private PartPhase phase;
	private Licytacja licytacja = null;
	public int answerTime = 120; // 2 min
	private StringBuilder messages;
	private int messagesCount;
	
	public PlayTable(String tableName, long tableId, long makerId, int humanPlayers, int computerPlayers, int startTokens, int entranceFee) throws Blad {
		this.tableId = tableId;
		this.makerId = makerId;
		this.bots = computerPlayers;
		this.humans = humanPlayers;
		this.tableName = tableName;
		this.tokensCount = startTokens;
		this.entranceFee = entranceFee;
		if(bots+humans > 4 || humans < 1) 
			throw new Blad("Maksymalna liczba graczy to 4, oraz co najmniej jeden z nich musi byc czlowiekiem.");
		players = new PlayerInfo[bots+humans];
		gracze = new Player[bots+humans];
		for(int i = humans; i < bots+humans; ++i) {
			players[i] = new PlayerInfo(new Bot2());
			gracze[i] = new Bot2();
		}
	}
	
	public boolean canStart() {
		int count = players.length;
		for(int i = 0; i < count; ++i)
			if(players[i] == null || !players[i].isReadyToStart())
				return false;
		return true;
	}
	
	public Player start() {
		if(!canStart() || started) return null;

		started = true;
		if(playerNum < 0)
			playerNum = 0;
		currentPlayerId = players[playerNum].getPlayerId();
		phase = PartPhase.ENTRANCE_FEE;
		messages = new StringBuilder();
		messagesCount = 0;
		return players[playerNum].getPlayer();
	}
	
	
	public Player next() {
		if(playersWithDoneAction == players.length) {
			return null;
		}
		
		playerNum = ++playerNum % players.length;
		currentPlayerId = players[playerNum].getPlayerId();
		return players[playerNum].getPlayer();
	}
	
	public boolean nextPhase() {
		phase = phase.next();
		if(phase == PartPhase.FIRST_BIDDING || phase == PartPhase.SECOND_BIDDING) {
			licytacja = new Licytacja(gracze, gracze.length);
		}
		else {
			phase = null;
		}
		playersWithDoneAction = 0; // nikt nie wykonał jeszcze akcji w tej fazie
		return true;
	}
	
	public void jumpToNextPhase() { // unsafe
		
	}
	
	public void removeFromTable(long userId, int userNum) {
		if(userId != players[userNum].getPlayerId()) return;
		PlayerInfo[] plinfo = new PlayerInfo[players.length - 1];
		Player[] playe = new Player[gracze.length - 1];
		for(int i = 0, j = 0; i < players.length; ++i, ++j) {
			if(i == userNum) { --j; continue; }
			plinfo[j] = players[i];
			playe[j] = gracze[i];
		}
		if(playerNum == userNum) {
			playerNum = (playerNum == 0) ? (gracze.length - 1) % playe.length :
				--playerNum % playe.length;
			licytacja.kolej = playerNum;
		}
		players = plinfo;
		gracze = playe;
		if(phase == PartPhase.FIRST_BIDDING || phase == PartPhase.SECOND_BIDDING) {
			licytacja.gracze = playe;
			licytacja.g = playe.length;
		}
	}
	
	public long getCurrentPlayerId() {
		return currentPlayerId;
	}
	
	public String getAvaibleActionsForCurrentPlayer() {
		switch(phase)
		{
		case ENTRANCE_FEE:
			return "1";
		case DEAL:
			return "2";
		case FIRST_BIDDING:
			return getPossibleBiddingActionsForCurrentPlayer();
		case CARD_EXCHANGE:
			return "3";
		case SECOND_BIDDING:
			return getPossibleBiddingActionsForCurrentPlayer();
		case SHOW_CARDS:
			return "4";
		case SHOW_WINNERS:
			return "5";
		default:
			return null;
		}
	}
	
	public String getPossibleBiddingActionsForCurrentPlayer() {
		if((phase != PartPhase.FIRST_BIDDING && phase != PartPhase.SECOND_BIDDING) || licytacja == null)
			return null;
		
		Player pl = players[playerNum].getPlayer();
		StringBuilder builder = new StringBuilder();
		
		builder.append("6"); // fold
		if(licytacja.canBet(pl, playerNum))
			builder.append(";7");
		
		if(licytacja.canCall(pl, playerNum))
			builder.append(";8");
		
		if(licytacja.canCheck(pl, playerNum))
			builder.append(";9");
		
		if(licytacja.canRaise(pl, playerNum))
			builder.append(";10");
		
		builder.append(";11"); // all-in
		
		return builder.toString();
	}
	
	private boolean removePoorPlayer(int neededCash) {
		for(int i = 0; i < players.length; ++i) {
			if(players[i].getTokens() < neededCash) {
				removeFromTable(players[i].getPlayerId(), i);
				return true;
			}
		}
		return false;
	}
	
	private void removeThisTable() {
		models.Tables.removeTable(tableId);
	}
	
	private Card[] dealCards(Deck deck) {
		int playerCount = players.length; 
		Card[] reka = new Card[playerCount];
		for(int i = 0; i < playerCount; ++i)
			reka[i] = deck.wypiszzTali();
		return reka;
	}
	
	public synchronized void performAction(int actionId, long userId, Object[] addParameter) {
		if(userId != currentPlayerId && actionId != 1 && actionId != 0) return;
		if(actionId >= 6 && actionId <= 11 && licytacja == null) return;
		switch(actionId) 
		{
		case 0:
			for(PlayerInfo pi : players) {
				if(pi.getPlayerId() == userId)
					pi.setReadyToStart(true);
			}
		case 1:
			if(userId == 0 && addParameter != null) {
				int wpisowe = (int)addParameter[0];
				while(removePoorPlayer(wpisowe));
				if(players.length < 2) removeThisTable();
				for(Player p : gracze)
					p.cash -= wpisowe;
				playerNum = new Random().nextInt(players.length); // pierwszy gracz
			}
			break; // wpłata wpisowego
		case 2:
			Deck d = null;
			if(talia == null) {
				d = new Deck();
				talia = d;
			}
			else
			{
				d = talia;
			}
			gracze[playerNum].ustawReke(dealCards(d));
			break; // gracz pobiera karty
		case 3:
			Player p = gracze[playerNum];
			for(int i = 0; i < addParameter.length; ++i) {
				p.zmien((int)addParameter[i]);
			}
			break; // wymiana kart przez gracza
		case 4:
			break; // pokaz wymienione karty
		case 5:
			break; // pokaz zwyciezcow
		case 6:
			licytacja.fold(gracze[playerNum], playerNum);
			break; // fold
		case 7:
			licytacja.bet(gracze[playerNum], playerNum, (int)addParameter[0]);
			break; // bet
		case 8:
			licytacja.call(gracze[playerNum], playerNum);
			break; // call
		case 9:
			licytacja.check(gracze[playerNum], playerNum);
			break; // check
		case 10:
			licytacja.raise(gracze[playerNum], playerNum, (int)addParameter[0]);
			break; // raise
		case 11:
			licytacja.all(gracze[playerNum], playerNum);
			break; // all-in	
		default:
			return;
		}
		++playersWithDoneAction;
	}
	
	public int getMissingPlayers() {
		int missingPlayers = humans;
		for(int i = 1; i < players.length; ++i) // 0. miejsce dla twórcy stołu
			if(players[i] != null && !players[i].isComputer())
				--missingPlayers;
		return missingPlayers; // jedno miejsce zarezerwowane dla twórcy
	}
	
	public boolean addHumanPlayer(long id, Human h) {
		return addHumanPlayer(id, new PlayerInfo(id, h));
	}
	
	public boolean addHumanPlayer(long id, PlayerInfo player) {
		if(getMissingPlayers() <= 0) return false;
		/*if(id == makerId) {
			players[0] = player;
			return true;
		}*/
		for(int i = 0; i < players.length; ++i) { // miejsce 0. zarezerwowane dla twórcy
			if(players[i] == null) {
				players[i] = player;
				gracze[i] = player.getPlayer();
				return true;
			}
		}
		return false;
	}

	public String getTableName() {
		return tableName;
	}

	public void setTableName(String tableName) {
		this.tableName = tableName;
	}

	public int getEntranceFee() {
		return entranceFee;
	}

	public void setEntranceFee(int entranceFee) {
		this.entranceFee = entranceFee;
	}

	public int getTokensCount() {
		return tokensCount;
	}

	public void setTokensCount(int tokensCount) {
		this.tokensCount = tokensCount;
	}

	public boolean isStarted() {
		return started;
	}

	public int getBots() {
		return bots;
	}

	public long getTableId() {
		return tableId;
	}

	public void setTableId(long tableId) {
		this.tableId = tableId;
	}

	public long getMakerId() {
		return makerId;
	}

	public void setMakerId(long makerId) {
		this.makerId = makerId;
	}
	
	public boolean isMissingPlayer() {
		for(int i = 0; i < gracze.length; ++i)
			if(gracze[i] == null)
				return true;
		return false;
	}
	
	public String getMessages() {
		if(messages == null) return null;
		return messages.toString();
	}
	
	public void addMessage(String message) {
		if(messages == null) return;
		messages.append(message + "\n");
		++messagesCount;
	}
	
	public int getMessagesCount() {
		return messagesCount;
	}
	
	public PlayerInfo getPlayerByServerId(long id) {
		for(PlayerInfo pl : players) {
			if(pl.getPlayerId() == id)
				return pl;
		}
		return null;
	}

}
